package com.chalk.controller;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.chalk.common.constant.CommonConstants;
import com.chalk.common.constant.SecurityConstants;
import com.chalk.common.exception.ServiceException;
import com.chalk.common.plugins.fastdfs.FastDFSClient;
import com.chalk.common.util.*;
import com.chalk.config.base.BaseController;
import com.chalk.config.security.JwtRedisUtil;
import com.chalk.dto.MiniLoginDto;
import com.chalk.dto.MiniQrDto;
import com.chalk.model.Member;
import com.chalk.service.MemberService;
import com.chalk.service.SysParamService;
import com.chalk.vo.MnWxLogin;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;


/**
 * 对接微信Controller
 *
 * @author mxq
 * @date 2019年04月22日 下午2:06:26
 */

@RestController
@RequestMapping(value = "/wx")
public class WxController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(WxController.class);

    @Value("${wx.appId}")
    private String appId;

    @Value("${wx.appsecret}")
    private String appsecret;

    @Value("${wx.scope}")
    private String scope;

    @Value("${wx.accessTokenExpiresIn}")
    private long accessTokenExpiresIn;

    @Value("${wx.redisHead}")
    private String redisHead;

    @Value("${wx-mini.appId}")
    private String miniAppId;

    @Value("${wx-mini.appsecret}")
    private String miniAppsecret;

    @Value("${fdfs.server-path}")
    private String serverPath;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RestTemplateUtil<String> restTemplateUtil;

    @Autowired
    private MemberService memberService;

    @Autowired
    private JwtRedisUtil jwtRedisUtil;

    @Autowired
    private SysParamService sysParamService;

    @Autowired
    private FastDFSClient fastDFSClient;

    /**
     * 微信授权使用的是OAuth2.0授权的方式
     * 一、用户同意授权，获取code(前端获取)
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/getCode")
    public Result getCode() {
        String redirectUrl = "btmcheck.com";
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + redirectUrl + "&response_type=code&scope=" + scope + "&state=0#wechat_redirect";
        String result = restTemplateUtil.doGet(url).getBody();
        return Result.success(result);
    }

    /**
     * 微信授权使用的是OAuth2.0授权的方式
     * 二、通过code换取网页授权access_token
     *
     * @param code
     * @return
     * @throws Exception
     */
    @GetMapping("/getAccessToken")
    public Result getAccessToken(String code) throws Exception {
        if (StringUtils.isBlank(code)) {
            return Result.fail("code不能为空");
        }
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code";
        String result = restTemplateUtil.doGet(url).getBody();
        return Result.success(result);
    }

    /**
     * 三、刷新access_token
     *
     * @param refreshToken
     * @return
     * @throws Exception
     */
    @GetMapping("/refreshToken")
    public Result refreshToken(String refreshToken) throws Exception {
        if (StringUtils.isBlank(refreshToken)) {
            return Result.fail("refreshToken不能为空");
        }
        String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + appId + "&grant_type=refresh_token&refresh_token=" + refreshToken;
        String result = restTemplateUtil.doGet(url).getBody();
        return Result.success(result);
    }

    /**
     * 获取微信用户信息
     *
     * @param accessToken
     * @param openid
     * @return
     * @throws Exception
     */
    @GetMapping("/getUserInfo")
    public Result getUserInfo(String accessToken, String openid) throws Exception {
        if (StringUtils.isBlank(accessToken)) {
            return Result.fail("accessToken不能为空");
        }
        if (StringUtils.isBlank(openid)) {
            return Result.fail("openid不能为空");
        }
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN";
        String result = restTemplateUtil.doGet(url).getBody();
        return Result.success(result);
    }

    /**
     * 微信小程序code登录
     *
     * @param mnWxLogin
     * @return
     */
    @RequestMapping("/miniWxLogin")
    public Result miniWxLogin(@RequestBody(required = false) MnWxLogin mnWxLogin, String jsCode, String nickname, String picUrl, HttpServletRequest request) {

        if (StringUtils.isBlank(jsCode) && StringUtils.isEmpty(mnWxLogin.getJsCode())) {
            return Result.fail("code不能为空！");
        }
        if (StringUtils.isBlank(nickname) && StringUtils.isEmpty(mnWxLogin.getNickname())) {
            return Result.fail("昵称不能为空！");
        }
        if (StringUtils.isBlank(picUrl) && StringUtils.isEmpty(mnWxLogin.getPicUrl())) {
            return Result.fail("c头像地址不能为空！");
        }
        String method = request.getMethod();
        if("GET".equals(method)){
            mnWxLogin = new MnWxLogin();
            mnWxLogin.setJsCode(jsCode);
            mnWxLogin.setNickname(nickname);
            mnWxLogin.setPicUrl(picUrl);
        }
        // 1.根据code获取openid
        String urlCode = "https://api.weixin.qq.com/sns/jscode2session?appid=" + miniAppId + "&secret=" + miniAppsecret + "&js_code=" + mnWxLogin.getJsCode() + "&grant_type=authorization_code";
        String resultCode = restTemplateUtil.doGet(urlCode).getBody();
        JSONObject jsonObject = JSONUtil.parseObj(resultCode);
        String openId = (String) jsonObject.get("openid");
        if (StringUtils.isBlank(openId)) {
            return Result.fail("openId获取失败" + resultCode);
        }
        System.out.println("openId: " + openId);
        // 4.将用户信息注册到自己的服务器数据库
        Member params = new Member();
        DateTime date = DateUtil.date();
        params.setOpenId(openId);
        Member member = memberService.selectOne(new EntityWrapper<>(params));
        if (member == null) {
            params.setCreateTime(date);
            params.setUpdateTime(date);
            params.setNickName(mnWxLogin.getNickname());
            params.setPicUrl(mnWxLogin.getPicUrl());
            boolean result = memberService.insert(params);
            if (result) {
                Map<String, Object> tokenAndRefreshToken = jwtRedisUtil.getTokenAndRefreshToken(openId, SecurityConstants.ROLE_LOGIN);
                return Result.success(ResultCode.SUCCESS.msg(), tokenAndRefreshToken);
            }
        }
        // 判断用户状态
        if (CommonConstants.IsDeleted.NOTDEL.getValue().equals(member.getIsDeleted())) {
            Map<String, Object> tokenAndRefreshToken = jwtRedisUtil.getTokenAndRefreshToken(openId, SecurityConstants.ROLE_LOGIN);
            return Result.success(ResultCode.SUCCESS.msg(), tokenAndRefreshToken);

        }
        return Result.fail("用户信息已被清除");
    }

    /**
     * 根据code获取sessionKey与openId
     *
     * @param code
     * @return
     */
    @GetMapping("/getSessionKey/{code}")
    public Result getSessionKey(@PathVariable String code) {
        if (StringUtils.isBlank(code)) {
            return Result.fail("参数不能为空！");
        }
        JSONObject sessionKeyOrOpenId = getSessionKeyOrOpenId(code);
        String sessionKey = (String) sessionKeyOrOpenId.get("session_key");
        if (StringUtils.isNotBlank(sessionKey)) {
            return Result.success(sessionKeyOrOpenId);
        }
        return Result.fail("获取sessionKey失败！");
    }

    /**
     * 微信小程序登录（已弃用）
     *
     * @param miniLoginDto
     * @return
     */
    //@PostMapping("/miniLogin")
    public Result miniLogin(@RequestBody @Valid MiniLoginDto miniLoginDto, BindingResult bresult) {
        beanValidator(bresult);
        // 2.解析加密数据
        String phoneNumber = null;
        JSONObject userencryptedData = AES.wxDecrypt(miniLoginDto.getEncryptedData(), miniLoginDto.getSessionKey(), miniLoginDto.getIv());
        if (StringUtils.isNotBlank((String) userencryptedData.get("phoneNumber"))) {
            phoneNumber = (String) userencryptedData.get("phoneNumber");
        }
        // 3.将用户信息注册到自己的服务器数据库
        Member params = new Member();
        DateTime date = DateUtil.date();
        params.setOpenId(miniLoginDto.getOpenId());
        Member member = memberService.selectOne(new EntityWrapper<>(params));
        if (member == null) {
            params.setCreateTime(date);
            params.setUpdateTime(date);
            params.setNickName(miniLoginDto.getNickName());
            params.setPicUrl(miniLoginDto.getAvatarUrl());
            params.setMobilePhone(phoneNumber);
            boolean result = memberService.insert(params);
            if (result) {
                Map<String, Object> tokenAndRefreshToken = jwtRedisUtil.getTokenAndRefreshToken(miniLoginDto.getOpenId(), SecurityConstants.ROLE_LOGIN);
                tokenAndRefreshToken.put("member", params);
                return Result.success(ResultCode.SUCCESS.msg(), tokenAndRefreshToken);
            }
        }
        // 判断用户状态
        if (CommonConstants.IsDeleted.NOTDEL.getValue().equals(member.getIsDeleted())) {
            Map<String, Object> tokenAndRefreshToken = jwtRedisUtil.getTokenAndRefreshToken(miniLoginDto.getOpenId(), SecurityConstants.ROLE_LOGIN);
            // 更新手机号
            member.setMobilePhone(phoneNumber);
            memberService.updateById(member);
            tokenAndRefreshToken.put("member", member);
            return Result.success(ResultCode.SUCCESS.msg(), tokenAndRefreshToken);

        }
        return Result.fail("登录失败！");
    }

    /**
     * 解密手机号
     *
     * @param miniLoginDto
     * @param bresult
     * @return
     */
    @PostMapping("/decodePhoneNumber")
    public Result decodePhoneNumber(@RequestBody @Valid MiniLoginDto miniLoginDto, BindingResult bresult) {
        beanValidator(bresult);
        // 解析加密数据
        String phoneNumber = null;
        JSONObject userencryptedData = AES.wxDecrypt(miniLoginDto.getEncryptedData(), miniLoginDto.getSessionKey(), miniLoginDto.getIv());
        if (StringUtils.isNotBlank((String) userencryptedData.get("phoneNumber"))) {
            phoneNumber = (String) userencryptedData.get("phoneNumber");
        }
        return Result.success(phoneNumber);
    }

    /**
     * 微信小程序向外暴露的的accessToekn获取
     *
     * @return
     */
    @GetMapping("/getMiniAccessToken")
    public Result getMiniAccessToken() {
        String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + miniAppId + "&secret=" + miniAppsecret;
        String resultToken = restTemplateUtil.doGet(tokenUrl).getBody();
        JSONObject jsonObject = JSONUtil.parseObj(resultToken);
        return Result.success(jsonObject);
    }

    /**
     * 获取小程序码（base64）
     *
     * @param miniQrDto
     * @param response
     * @return
     */
    @PostMapping("/getminiqrQr")
    public Result getminiqrQr(@Validated MiniQrDto miniQrDto, HttpServletResponse response) {
        RestTemplate rest = new RestTemplate();

        String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getMiniToken();
        Map<String, Object> param = new HashMap<>();
        param.put("scene", miniQrDto.getScene());
        param.put("page", miniQrDto.getPage());
        param.put("width", 430);
        param.put("auto_color", false);
        Map<String, Object> lineColor = new HashMap<>();
        lineColor.put("r", 0);
        lineColor.put("g", 0);
        lineColor.put("b", 0);
        param.put("line_color", lineColor);
        log.info("调用生成微信URL接口传参:" + param);
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.set("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
        HttpEntity requestEntity = new HttpEntity(param, headers);
        ResponseEntity<byte[]> entity = rest.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]);
        try {
            JSONObject jsonObject = JSONUtil.parseObj(new String(entity.getBody()));
            // 请求错误时返回Json对象
            if (StringUtils.isNotBlank(jsonObject.get("errcode").toString())) {
                return Result.fail(jsonObject);
            }
        } catch (JSONException e) {
            logger.info("获取小程序码成功");
        }
        // 给前端返回
        Map<String, Object> result = new HashMap<>(2);
        InputStream inputStream = null;
        String imageUrl = null;
        try {
            inputStream = new ByteArrayInputStream(entity.getBody());
            imageUrl = fastDFSClient.uploadStream(inputStream, entity.getBody().length, "112.png");
            result.put("imageUrl", serverPath + imageUrl);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 成功返回图片二进制  转为base64给前端
        String s = Base64.encodeBase64String(entity.getBody());
        result.put("baseData", "data:image/png;base64," + s);
        return Result.success(result);

    }

    /**
     * 获取小程序码(返回二进制流)
     *
     * @param miniQrDto
     * @param response
     * @return
     */
    @PostMapping("/getminiStreamQr")
    public void getminiStreamQr(@Validated MiniQrDto miniQrDto, HttpServletResponse response) {
        RestTemplate rest = new RestTemplate();

        String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getMiniToken();
        Map<String, Object> param = new HashMap<>();
        param.put("scene", miniQrDto.getScene());
        param.put("page", miniQrDto.getPage());
        param.put("width", 430);
        param.put("auto_color", false);
        Map<String, Object> lineColor = new HashMap<>();
        lineColor.put("r", 0);
        lineColor.put("g", 0);
        lineColor.put("b", 0);
        param.put("line_color", lineColor);
        log.info("调用生成微信URL接口传参:" + param);
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.set("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
        HttpEntity requestEntity = new HttpEntity(param, headers);
        ResponseEntity<byte[]> entity = rest.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]);
        try {
            JSONObject jsonObject = JSONUtil.parseObj(new String(entity.getBody()));
            // 请求错误时返回Json对象
            if (StringUtils.isNotBlank(jsonObject.get("errcode").toString())) {
                response.setStatus(6000);
            }
        } catch (JSONException e) {
            logger.info("获取小程序码成功");
        }
        // 转为base64
//		String s = Base64.encodeBase64String(entity.getBody());
//		logger.info("Base64:" + "data:image/png;base64," + s);
        response.setDateHeader("Expires", 0L);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/png");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            outputStream.write(entity.getBody(), 0, entity.getBody().length);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 微信小程序获取accessToken
     *
     * @return
     */
    private String getMiniToken() {
        String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + miniAppId + "&secret=" + miniAppsecret;
        String resultToken = restTemplateUtil.doGet(tokenUrl).getBody();
        JSONObject jsonObject = JSONUtil.parseObj(resultToken);
        return (String) jsonObject.get("access_token");
    }

    /**
     * 获取openid和sessionKey方法
     *
     * @param code
     * @return
     */
    private JSONObject getSessionKeyOrOpenId(String code) {
        String tokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
        tokenUrl = tokenUrl.replace("APPID", miniAppId).replace("SECRET", miniAppsecret).replace("JSCODE", code);
        String resultToken = restTemplateUtil.doGet(tokenUrl).getBody();
        JSONObject jsonObject = JSONUtil.parseObj(resultToken);
        return jsonObject;
    }


    /**
     * 获取token
     *
     * @return
     * @throws Exception
     */
    private String getToken() {
        String jssdkTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appsecret;
        String access_token = redisUtil.get(redisHead + jssdkTokenUrl, String.class);
        if (StringUtils.isBlank(access_token)) {
            // 在微信服务端获取accesstoken
            String jssdkToken = restTemplateUtil.doGet(jssdkTokenUrl).getBody();
            JsonObject jssdkTokenJson = GsonUtil.jsonToJsonObject(jssdkToken);
            // 用Gson获取 access_token
            JsonElement accessTokenJsonElement = jssdkTokenJson.get("access_token");
            if (accessTokenJsonElement == null) {
                return access_token;
            }
            access_token = accessTokenJsonElement.getAsString();
            redisUtil.set(redisHead + jssdkTokenUrl, access_token, accessTokenExpiresIn);
        }
        return access_token;
    }

    /**
     * 内容安全验证（文本）
     * @param map
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "内容安全验证（文本）", notes = "内容安全验证（文本）", response = Result.class)
    @PostMapping("/mesCheck")
    public Result mesCheck(@RequestBody Map<String, Object> map) throws Exception {
        /*获取access_token*/
        String token = getMiniToken();

        JSONObject jsonObject = JSONUtil.parseFromMap(map);
        String content = JSONUtil.toJsonStr(jsonObject);
        String url = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token="+token;
        ResponseEntity<String> responseEntity = restTemplateUtil.doPost(url, content, String.class);
        String body = responseEntity.getBody();
        JSONObject resultCode = JSONUtil.parseObj(body);
        Integer errcode = (Integer) resultCode.get("errcode");
        if(0 == errcode){
            return Result.success("内容正常");
        }else if(87014 == errcode){
            return Result.fail("含有违法违规内容");
        }else{
            throw new ServiceException(ResultCode.SYS_ERROR);
        }
    }





    /**
     * 内容安全验证（音视频，图片）
     * @param map
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "内容安全验证（音视频，图片）", notes = "内容安全验证（音视频，图片）", response = Result.class)
    @PostMapping("/mediaCheckAsync")
    public Result mediaCheckAsync(@RequestBody Map<String, Object> map) throws Exception {
        /*获取access_token*/
        String token = getMiniToken();

        JSONObject jsonObject = JSONUtil.parseFromMap(map);
        String content = JSONUtil.toJsonStr(jsonObject);
        String url = "https://api.weixin.qq.com/wxa/media_check_async?access_token="+token;
        ResponseEntity<String> responseEntity = restTemplateUtil.doPost(url, content, String.class);
        String body = responseEntity.getBody();
        JSONObject resultCode = JSONUtil.parseObj(body);
        Integer errcode = (Integer) resultCode.get("errcode");
        if(0 == errcode){
            return Result.success("内容正常");
        }else{
//            throw new ServiceException(ResultCode.SYS_ERROR);
            return Result.fail();
        }
    }

}
